package cn.itsource.ymcc.web.controller;

import cn.itsource.ymcc.doc.CourseDoc;
import cn.itsource.ymcc.query.CourseDocQuery;
import cn.itsource.ymcc.repository.CourseDocRepository;
import cn.itsource.ymcc.result.JsonResult;
import cn.itsource.ymcc.resultmap.HighlightResultMapper;
import cn.itsource.ymcc.vo.AggrPageList;
import cn.itsource.ymcc.vo.AggrTermsBucket;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 全文检索服务接口
 *  批量新增/修改
 *  批量删除
 *  搜索
 */
@RestController
@RequestMapping("/search")
public class SearchController {

    @Autowired
    private CourseDocRepository courseDocRepository;
    @Autowired
    private ElasticsearchRestTemplate restTemplate;
    @Autowired
    private HighlightResultMapper highlightResultMapper;


    /**
     * 批量新增/修改
     * @param courseDocs
     * @return
     */
    @PostMapping("/saveBatch")
    public JsonResult saveBatch(@RequestBody List<CourseDoc> courseDocs){
        courseDocRepository.saveAll(courseDocs);
        return JsonResult.me();
    }

    /**
     * 批量删除
     * @param courseIds
     * @return
     */
    @DeleteMapping("/deleteBatch")
    public JsonResult deleteBatch(@RequestBody List<Long> courseIds){
        courseIds.forEach(cid -> courseDocRepository.deleteById(cid));
        return JsonResult.me();
    }

    /**
     * 从ES索引库中搜索课程数据
     *  支持高级查询+分页+排序
     * GET ymcc/course/_search
     * {
     *   "query": {
     *     "bool": {
     *       "filter": [
     *         {
     *           "match": {
     *             "name": "java"
     *           }
     *         }
     *       ]
     *     }
     *   },
     *   "from": 0,
     *   "size": 12,
     *   "sort": [
     *     {
     *       "id": {
     *         "order": "desc"
     *       }
     *     }
     *   ]
     * }
     * 查询与过滤的方式有哪些？
     *  1.match   分词匹配 相当于 like    ------ 只能用在Text字符串类型的字段上面
     *  2.term  不分词精确匹配  相当于 等于   ----- 类型不限
     *  3.range 范围匹配            ----- 可以比较大小的类型 整数、浮点数
     *  4.wildcard  通配符匹配      ----- 只能用在Keyword字符串类型的字段上面
     *  5.prefix 前缀匹配           ----- 只能用在Keyword字符串类型的字段上面
     *  6.exists和missing        ----- 存在：搜索某字段非空；缺失：搜索某字段为空的
     * @param query
     * @return
     */
    @PostMapping("/queryCourse")
    public JsonResult queryCourse(@RequestBody CourseDocQuery query){
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder bool = QueryBuilders.boolQuery();
        //1.多个单分支语句动态添加搜索条件
        if(StringUtils.hasText(query.getKeyword())){
            //课程名称分词匹配  相当于 like
            bool.filter().add(QueryBuilders.matchQuery("name", query.getKeyword()));
        }
        if(query.getCourseTypeId() != null){
            //精确匹配  不分词匹配  相当于数据库中的 等于
            bool.filter().add(QueryBuilders.termQuery("courseTypeId", query.getCourseTypeId()));
        }
        if(StringUtils.hasText(query.getCourseTypeName())){
            //不分词匹配
            bool.filter().add(QueryBuilders.termQuery("courseTypeName", query.getCourseTypeName()));
        }
        if(query.getGradeId() != null){
            //精确匹配  不分词匹配  相当于数据库中的 等于
            bool.filter().add(QueryBuilders.termQuery("gradeId", query.getGradeId()));
        }
        if(StringUtils.hasText(query.getGradeName())){
            //不分词匹配
            bool.filter().add(QueryBuilders.termQuery("gradeName", query.getGradeName()));
        }
        if(query.getPriceMin() != null){
            //范围匹配
            bool.filter().add(QueryBuilders.rangeQuery("price").gte(query.getPriceMin()));
        }
        if(query.getPriceMax() != null){
            //范围匹配
            bool.filter().add(QueryBuilders.rangeQuery("price").lte(query.getPriceMax()));
        }
        queryBuilder.withQuery(bool);

        //2.分页：SpringDataElasticsearch分页的当前页码是从0开始计算的
        queryBuilder.withPageable(PageRequest.of(query.getPageNo()-1, query.getPageSize()));

        //3.排序
        if(StringUtils.hasText(query.getOrderField())){
            FieldSortBuilder sortBuilder = SortBuilders
                    .fieldSort(query.getOrderField())
                    .order(SortOrder.DESC);
            if("asc".equalsIgnoreCase(query.getOrderType())){
                sortBuilder.order(SortOrder.ASC);
            }
            queryBuilder.withSort(sortBuilder);
        }

        //4.添加关键字高亮：只能用在分词的字符串类型上
        HighlightBuilder.Field  highlightField = new HighlightBuilder.Field("name")
                .preTags("<b style='color:red'>")
                .postTags("</b>");
        queryBuilder.withHighlightFields(highlightField);

        //5.添加聚合统计条件
        //聚合查询,按照课程等级名字聚合，按照数量倒排
        queryBuilder.addAggregation(AggregationBuilders
                .terms("gradeNameAgg") //聚合名字  起一个别名
                .field("gradeName")	//聚合的字段  以哪个字段进行分组
                .order(BucketOrder.count(false)).size(20));//排序后取前20条
        //聚合查询,按照课程类别名字聚合，按照数量倒排
        queryBuilder.addAggregation(AggregationBuilders
                .terms("courseTypeNameAgg") //聚合名字  起一个别名
                .field("courseTypeName")	//聚合的字段  以哪个字段进行分组
                .order(BucketOrder.count(false)).size(20));//排序后取前20条

        //6.执行搜索
        //Page<CourseDoc> page = courseDocRepository.search(queryBuilder.build());
        AggregatedPage<CourseDoc> page = restTemplate.queryForPage(queryBuilder.build(), CourseDoc.class, highlightResultMapper);
        System.out.println("当前页码："+page.getNumber());
        System.out.println("每页条数："+page.getSize());
        System.out.println("总条数："+page.getTotalElements());
        System.out.println("总页数："+page.getTotalPages());
        System.out.println("当前页条数："+page.getNumberOfElements());
        //当前页的数据
        List<CourseDoc> content = page.getContent();

        //7.提取聚合统计结果
        Map<String,List<AggrTermsBucket>> aggrMap = getAggrResult(page);

        //8.返回我们自己的pageList
        AggrPageList<CourseDoc> pageList = new AggrPageList<>(page.getTotalElements(), content, aggrMap);
        return JsonResult.me(pageList);
    }

    private Map<String, List<AggrTermsBucket>> getAggrResult(AggregatedPage<CourseDoc> page) {
        //用来封装聚合结果
        Map<String,List<AggrTermsBucket>> aggrMap = new HashMap<>();
        //遍历聚合结果
        Aggregations aggregations = page.getAggregations();
        Map<String, Aggregation> stringAggregationMap = aggregations.asMap();
        Set<Map.Entry<String, Aggregation>> entries = stringAggregationMap.entrySet();
        entries.forEach(e->{
            //集合的名字  聚合统计的别名
            String key = e.getKey();
            //集合的值的处理
            ParsedStringTerms value = (ParsedStringTerms)e.getValue();
            List<? extends Terms.Bucket> buckets = value.getBuckets();
            //使用lomda 把  List<StringTerms.Bucket>  转成  List<AggrTermsVo>
            List<AggrTermsBucket> aggrTermsVos = buckets.stream().map(bucket -> {
                return new AggrTermsBucket(bucket.getKeyAsString(), bucket.getDocCount());
            }).collect(Collectors.toList());
            aggrMap.put(key, aggrTermsVos);
        });
        return aggrMap;
    }

    /**
     * 从订单确认页面发送请求，来查询课程数据
     * @param courseIds
     * @return
     */
    @PostMapping("/courseDocInfo")
    public JsonResult courseDocInfo(@RequestBody List<Long> courseIds){
        List<CourseDoc> courseDocs = (List<CourseDoc>) courseDocRepository.findAllById(courseIds);
        return JsonResult.me(courseDocs);
    }

}
